﻿/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2012 Junhua
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of 
 * this software and associated documentation files (the "Software"), to deal in the 
 * Software without restriction, including without limitation the rights to use, copy, 
 * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
 * and to permit persons to whom the Software is furnished to do so, subject to the 
 * following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all 
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

namespace Reading.Controls
{
    using System;
    using System.Collections;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Markup;

    [TemplatePart(Name = ItemsContainer, Type = typeof(Canvas))]
    public class ReadingControl : Control
    {
        const string ItemsContainer = "ItemsContainer";

        ScrollViewer[] viewers;

        ContentControl[] viewerContents;

        Canvas container;

        int index = 1;

        double y, x;

        bool canNext, canPrevious;

        public IList Items { get; private set; }

        public ReadingControl()
        {
            base.DefaultStyleKey = typeof(ReadingControl);

            viewers = new ScrollViewer[3];
            viewerContents = new ContentControl[3];
            Items = new Collection<object>();
            selectedIndex = -1;

            Touch.FrameReported += OnTouchFrameReported;
        }

        int selectedIndex;
        public int SelectedIndex
        {
            get { return selectedIndex; }
            private set
            {
                if (value != selectedIndex)
                {
                    var old = selectedIndex;
                    selectedIndex = value;

                    if (SelectionChanged != null)
                    {
                        try
                        {
                            SelectionChanged(this, new SelectionChangedEventArgs(new[] { Items[old] }, new[] { Items[selectedIndex] }));
                        }
                        catch (Exception ex)
                        {
                            System.Diagnostics.Debug.WriteLine(ex.ToString());
                        }
                    }

                    if (selectedIndex > 0)
                    {
                        canPrevious = true;
                    }
                    else
                    {
                        canPrevious = false;
                    }

                    if (selectedIndex < Items.Count - 1)
                    {
                        canNext = true;
                    }
                    else
                    {
                        canNext = false;
                    }
                }
            }
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            container = base.GetTemplateChild(ItemsContainer) as Canvas;

            if (container != null)
            {
                for (int i = 0; i < container.Children.Count; i++)
                {
                    ScrollViewer viewer = container.Children[i] as ScrollViewer;

                    // assigned width.
                    viewer.Width = Application.Current.Host.Content.ActualWidth;
                    viewerContents[i] = (viewer.Content as StackPanel).Children[0] as ContentControl;
                    viewers[i] = viewer;
                }
            }
        }

        void OnTouchFrameReported(object sender, TouchFrameEventArgs e)
        {
            if (this.container == null) { return; }

            var ps = e.GetTouchPoints(this.container);
            if (ps.Count > 0)
            {
                var p = ps[0];

                if (p.Action == TouchAction.Down)
                {
                    y = p.Position.Y;
                    x = p.Position.X;
                }

                if (p.Action == TouchAction.Up)
                {
                    if (Math.Abs(p.Position.Y - y) < 300)
                    {
                        if (p.Position.X - x > 100)
                        {
                            ChangeToPrevious();
                        }
                        else if (p.Position.X - x < -100)
                        {
                            ChangeToNext();
                        }
                    }
                }
            }
        }

        #region ItemsSource DependencyProperty

        /// <summary>
        /// Gets or sets the data template that defines
        /// the content information.
        /// </summary>
        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        /// <summary>
        /// Identifies the ItemsSourceProperty dependency property.
        /// </summary>
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(ReadingControl), new PropertyMetadata(null, OnItemsSourcePropertyChanged));

        /// <summary>
        /// ItemsSource changed handler.
        /// </summary>
        /// <param name="obj">The dependency object.</param>
        /// <param name="e">The event information.</param>
        private static void OnItemsSourcePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            ReadingControl control = obj as ReadingControl;
            if (control != null)
            {
                control.OnItemsSourcePropertyChanged(e);
            }
        }

        private void OnItemsSourcePropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            IList list = e.NewValue as IList;
            if (list != null && list.Count > 0)
            {
                Items = new Collection<object>();

                foreach (object item in list)
                {
                    Items.Add(item);
                }

                viewerContents[index].Content = Items[0];

                if (list.Count >= 2)
                {
                    viewerContents[index + 1].Content = Items[1];
                    canNext = true;
                }

                SelectedIndex = 0;
            }

            INotifyCollectionChanged notifyCollection = e.NewValue as INotifyCollectionChanged;
            if (notifyCollection != null)
            {
                notifyCollection.CollectionChanged += OnCollectionChanged;
            }
        }

        #endregion

        #region ReadingTemplate DependencyProperty

        /// <summary>
        /// Gets or sets the data template that defines
        /// the content information.
        /// </summary>
        public DataTemplate ReadingTemplate
        {
            get { return (DataTemplate)GetValue(ReadingTemplateProperty); }
            set { SetValue(ReadingTemplateProperty, value); }
        }

        /// <summary>
        /// Identifies the ReadingTemplateProperty dependency property.
        /// </summary>
        public static readonly DependencyProperty ReadingTemplateProperty =
            DependencyProperty.Register("ReadingTemplate", typeof(DataTemplate), typeof(ReadingControl), new PropertyMetadata(null, OnReadingTemplatePropertyChanged));

        /// <summary>
        /// PullPanelTemplate changed handler.
        /// </summary>
        /// <param name="obj">The dependency object.</param>
        /// <param name="e">The event information.</param>
        private static void OnReadingTemplatePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            ReadingControl control = obj as ReadingControl;
            if (control != null)
            {
                control.OnReadingTemplatePropertyChanged(e);
            }
        }

        private void OnReadingTemplatePropertyChanged(DependencyPropertyChangedEventArgs e)
        {
        }

        #endregion

        #region public members

        public void Next()
        {
            ChangeToNext();
        }

        public void Previous()
        {
            ChangeToPrevious();
        }

        #endregion

        #region events

        public event EventHandler<SelectionChangedEventArgs> SelectionChanged;

        #endregion

        void ChangeToNext()
        {
            if (!canNext) { return; }

            int rIndex = ((index + 1) % 3);
            int lIndex = (index % 3);

            Storyboards ss = Storyboards.CreateRightToLeft(viewers[rIndex], viewers[lIndex]);
            ss.Completed += (s, e) => viewers[lIndex].ScrollToVerticalOffset(0);
            ss.Begin();

            index = (++index) % 3;
            System.Diagnostics.Debug.WriteLine(string.Format("index:{0}, {1} <- {2}", index, lIndex, rIndex));

            // update items
            SelectedIndex++;
            if (SelectedIndex < Items.Count - 1)
            {
                int next = (index + 1) % 3;
                viewerContents[next].Content = Items[SelectedIndex + 1];
            }
        }

        void ChangeToPrevious()
        {
            if (!canPrevious) { return; }

            int lIndex = (index - 1 % 3);
            int rIndex = (index % 3);
            if (lIndex < 0) { lIndex += 3; }

            Storyboards ss = Storyboards.CreateLeftToRight(viewers[lIndex], viewers[rIndex]);
            ss.Completed += (s, e) => viewers[rIndex].ScrollToVerticalOffset(0);
            ss.Begin();

            index--;
            if (index < 0) { index += 3; }
            System.Diagnostics.Debug.WriteLine(string.Format("index:{0}, {1} -> {2}", index, lIndex, rIndex));

            // update items
            SelectedIndex--;
            if (SelectedIndex > 0)
            {
                int previous = (index - 1) % 3;
                if (previous < 0)
                {
                    previous += 3;
                }
                viewerContents[previous].Content = Items[SelectedIndex - 1];
            }
        }

        void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    break;
                case NotifyCollectionChangedAction.Remove:
                    break;
                case NotifyCollectionChangedAction.Replace:
                    break;
                case NotifyCollectionChangedAction.Reset:
                    break;
            }
        }
    }
}
